<?php

namespace yifang\lib;

use yifang\App;

/**
 * 语言类
 */
class Lang
{
    protected $app;
    /**
     * 配置参数
     * @var array
     */
    protected $config = [
        // 默认语言
        'default_lang'    => 'zh-cn',
        // 允许的语言列表
        'allow_lang_list' => [],
        // 是否使用Cookie记录
        'use_cookie'      => true,
        // 扩展语言包
        'extend_list'     => [],
        // 多语言cookie变量
        'cookie_var'      => 'yifang_lang',
        // 多语言header变量
        'header_var'      => 'yifang-lang',
        // 多语言自动侦测变量名
        'detect_var'      => 'lang',
        // Accept-Language转义为对应语言包名称
        'accept_language' => [
            'zh-hans-cn' => 'zh-cn',
        ],
        // 是否支持语言分组
        'allow_group'     => false,
    ];

    /**
     * 多语言信息
     * @var array
     */
    protected $lang=array();

    /**
     * 插件多语言信息
     * @var array
     */
    protected $pluginLang=array();

    /**
     * 当前语言
     * @var string
     */
    private $range = 'zh-cn';

    /**
     * 构造方法
     */
    public function __construct(App $app, array $config = [])
    {
        $this->config = array_merge($this->config, array_change_key_case($config));
        $this->range  = $this->config['default_lang'];
        $this->app    = $app;
    }

    /**
     * 获取实例方法
     */
    public static function __make(App $app, Config $config)
    {
        return new static($app, $config->get('lang'));
    }

    /**
     * 获取当前语言配置
     */
    public function getConfig()
    {
        return $this->config;
    }

    /**
     * 设置当前语言
     */
    public function setLangSet($lang)
    {
        $this->range = $lang;
    }

    /**
     * 获取当前语言
     */
    public function getLangSet()
    {
        return $this->range;
    }

    /**
     * 获取默认语言
     */
    public function defaultLangSet()
    {
        return $this->config['default_lang'];
    }

    /**
     * 切换语言
     * @param string $langset 语言
     */
    public function switchLangSet($langset)
    {
        if (empty($langset)) {
            return;
        }
        $this->setLangSet($langset);

        // 加载应用语言包
        $files = glob($this->app->getAppPath() . 'lang' . DIRECTORY_SEPARATOR . $langset . '.*');
        $this->load($files);

        // 加载扩展（自定义）语言包
        $list = $this->app->config->get('lang.extend_list', []);
        if (isset($list[$langset])) {
            $this->load($list[$langset]);
        }
    }

    /**
     * 加载语言定义(不区分大小写)
     * @access public
     * @param string|array $file  语言文件
     * @param string       $range 语言作用域
     * @return array
     */
    public function load($file, $range = '')
    {
        $range = $range ?: $this->range;
        if (!isset($this->lang[$range])) {
            $this->lang[$range] = [];
        }
        $lang = [];
        foreach ((array) $file as $name) {
            if (is_file($name)) {
                $result = $this->parse($name);
                $lang   = array_change_key_case($result) + $lang;
            }
        }
        if (!empty($lang)) {
            $this->lang[$range] = $lang + $this->lang[$range];
        }
        return $this->lang[$range];
    }

    /**
     * 解析语言文件
     * @access protected
     * @param string $file 语言文件名
     * @return array
     */
    protected function parse($file)
    {
        $type = pathinfo($file, PATHINFO_EXTENSION);
        switch ($type) {
            case 'php':
                $result = include $file;
                break;
            case 'yml':
            case 'yaml':
                if (function_exists('yaml_parse_file')) {
                    $result = yaml_parse_file($file);
                }
                break;
            case 'json':
                $data = file_get_contents($file);
                if (false !== $data) {
                    $data = json_decode($data, true);
                    if (json_last_error() === JSON_ERROR_NONE) {
                        $result = $data;
                    }
                }
                break;
        }
        return isset($result) && is_array($result) ? $result : [];
    }

    /**
     * 判断是否存在语言定义(不区分大小写)
     * @access public
     * @param string|null $name  语言变量
     * @param string      $range 语言作用域
     * @return bool
     */
    public function has($name, $range = '')
    {
        $range = $range ?: $this->range;
        if (false === strpos($name, '.') && !isset($this->lang[$range][strtolower($name)])) {
            return false;
        }
        return !is_null($this->get($name,[],$range));
    }

    /**
     * 获取语言定义(不区分大小写)
     * @access public
     * @param string|null $name  语言变量
     * @param array       $vars  变量替换
     * @param string      $range 语言作用域
     * @return mixed
     */
    public function get($name = null, array $vars = [], $range = '')
    {
        $range = $range ?: $this->range;
        if (!isset($this->lang[$range])) {
            $this->switchLangSet($range);
        }
        // 空参数返回所有定义
        if (is_null($name)) {
            return $this->lang[$range] ?? [];
        }
        $name    = explode('.', $name);
        $name[0] = strtolower($name[0]);
        $value  = $this->lang[$range];
        // 按.拆分成多维数组进行判断
        foreach ($name as $val) {
            if (isset($value[$val])) {
                $value = $value[$val];
            } else {
                $value= implode(".",$name);
                break;
            }
        }
        // 变量解析
        if (!empty($vars) && is_array($vars)) {
            if (key($vars) === 0) {
                // 数字索引解析
                array_unshift($vars, $value);
                $value = call_user_func_array('sprintf', $vars);
            } else {
                // 关联索引解析
                $replace = array_keys($vars);
                foreach ($replace as &$v) {
                    $v = "{:{$v}}";
                }
                $value = str_replace($replace, $vars, $value);
            }
        }
        return $value;
    }

    /**
     * 切换语言
     * @param string $langset 语言
     */
    public function switchPluginLangSet($prefix,$dirs)
    {
        if($dirs){
            foreach($dirs as $dir){
                $dir=rtrim(parseDir($dir), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
                $files=glob($dir."*");
                if($files){
                    $this->loadPlugin($prefix,$files);
                }
            }
        }
    }

    /**
     * 加载语言定义(不区分大小写)
     * @access public
     * @param string|array $file  语言文件
     * @param string       $range 语言作用域
     * @return array
     */
    public function loadPlugin($prefix,$file)
    {
        $range =$this->range;
        if (!isset($this->pluginLang[$prefix][$range])) {
            $this->pluginLang[$prefix][$range] = [];
        }
        $lang = [];
        foreach ((array) $file as $name) {
            if (is_file($name)) {
                $result = $this->parse($name);
                $lang   = array_change_key_case($result) + $lang;
            }
        }
        if (!empty($lang)) {
            $this->pluginLang[$prefix][$range] = $lang + $this->pluginLang[$prefix][$range];
        }
        return $this->pluginLang[$prefix][$range];
    }

    /**
     * 判断是否存在语言定义(不区分大小写)
     * @access public
     * @param string|null $name  语言变量
     * @param string      $range 语言作用域
     * @return bool
     */
    public function hasPlugin($prefix,$name)
    {
        $range = $this->range;
        if (false === strpos($name, '.') && !isset($this->pluginLang[$prefix][$range][strtolower($name)])) {
            return false;
        }
        return !is_null($this->getPlugin($prefix,$name,[]));
    }

    /**
     * 获取语言定义(不区分大小写)
     * @access public
     * @param string|null $name  语言变量
     * @param array       $vars  变量替换
     * @param string      $range 语言作用域
     * @return mixed
     */
    public function getPlugin($prefix,$name = null, array $vars = [])
    {
        $range = $this->range;
        // 空参数返回所有定义
        if (is_null($name)) {
            return $this->pluginLang[$prefix][$range] ?? [];
        }
        $name    = explode('.', $name);
        $name[0] = strtolower($name[0]);

        $value  = $this->pluginLang[$prefix][$range];
        // 按.拆分成多维数组进行判断
        foreach ($name as $val) {
            if (isset($value[$val])) {
                $value = $value[$val];
            } else {
                $value= implode(".",$name);
                break;
            }
        }
        // 变量解析
        if (!empty($vars) && is_array($vars)) {
            if (key($vars) === 0) {
                // 数字索引解析
                array_unshift($vars, $value);
                $value = call_user_func_array('sprintf', $vars);
            } else {
                // 关联索引解析
                $replace = array_keys($vars);
                foreach ($replace as &$v) {
                    $v = "{:{$v}}";
                }
                $value = str_replace($replace, $vars, $value);
            }
        }
        return $value;
    }
}